﻿using System.Reflection;
using System.Web.Routing;

namespace System.Web.Mvc
{
    internal static class VirtualPathBuilder
    {
        private static ActionRouteDescriptorCache s_cache = new ActionRouteDescriptorCache();

        public static VirtualPathData GetVirtualPath(this RouteCollection routeCollection, RequestContext requestContext, MethodInfo action, object[] args)
        {
            return routeCollection.GetVirtualPath(requestContext, action, args, (RouteValueDictionary)null);
        }

        public static VirtualPathData GetVirtualPath(this RouteCollection routeCollection, RequestContext requestContext, MethodInfo action, object[] args, RouteValueDictionary routeValues)
        {
            ActionRouteDescriptor descriptor = s_cache.Get(action);

            RouteValueDictionary rvd = new RouteValueDictionary();

            rvd.Add("controller", descriptor.ControllerName);
            rvd.Add("action", descriptor.Name);

            AddParameterValues(requestContext, rvd, action, args, descriptor);

            if (routeValues != null)
            {
                foreach (var pair in routeValues)
                {
                    rvd.Add(pair.Key, pair.Value);
                }
            }

            if (String.IsNullOrEmpty(descriptor.RouteName))
            {
                return routeCollection.GetVirtualPath(requestContext, rvd);
            }
            else
            {
                return routeCollection.GetVirtualPath(requestContext, descriptor.RouteName, rvd);
            }
        }

        public static VirtualPathData GetVirtualPath(this RouteCollection routeCollection, RequestContext requestContext, MethodInfo action, object[] args, object routeValues)
        {
            return routeCollection.GetVirtualPath(requestContext, action, args, routeValues == null ? null : new RouteValueDictionary(routeValues));
        }

        private static void AddParameterValues(RequestContext requestContext, RouteValueDictionary rvd, MethodInfo action, object[] args, ActionRouteDescriptor descriptor)
        {
            var parameters = action.GetParameters();

            for (int i = 0; i < parameters.Length; i++)
            {
                var routeBinder = descriptor.GetBinder(i);
                var param = parameters[i];

                if (routeBinder == null)
                {
                    rvd.Add(param.Name, args[i]);
                }
                else
                {
                    var bindingContext = new RouteBindingContext
                    {
                        Model = args[i],
                        ModelName = param.Name,
                        ModelType = param.ParameterType
                    };

                    var values = routeBinder.BindRoute(requestContext, bindingContext);

                    if (values != null && values.Count > 0)
                    {
                        foreach (var pair in values)
                        {
                            rvd.Add(pair.Key, pair.Value);
                        }
                    }
                }
            }
        }
    }
}
